// sort.cpp : Defines the entry point for the console application.
//

//#include "stdafx.h"


#include <windows.h>
#include <stdio.h>
#include <vector>
#include <iostream>
//#include <algorithm>

//#include <sys/types.h>
#include <sys/stat.h>
//#include <sys/mman.h>
#include <fcntl.h>
//#include <unistd.h>
//#include <assert.h>
//#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <process.h>
#include <conio.h>
#include <limits.h>


#define give_msg (*logg)<<onelog
#define max_filesize 1024*1024*128*11
#define max_arraysize 1024*1024*128
const int chunk_size=1024*1024;
const int max_numb_length=10;
enum levels{ ERR = 0, WARN, INF };

class onelog{
    int level;
public:
    onelog(int lvl, const char* msg, ...);
    char* type();
    const char* message;
};

 onelog::onelog(int lvl, const char* msg, ...){
     char *buf = new char[255];
    va_list argList;
    va_start(argList, msg);
    vsnprintf (buf, 100, msg, argList);
    va_end(argList);
    message=buf;
    level=lvl;
};
char* onelog::type()
{
    char* mes;
    switch (level){
    case ERR:
        mes="error: ";
        break;
    case WARN:
        mes="warning: ";
        break;
    case INF:
        mes="information: ";
        break;
    }
    return mes;

}

class Logger{
public:
	FILE *out;
	std::vector<onelog> log_array;
	Logger();
	void adding(onelog log);
	Logger& operator<<(onelog log);
	~Logger();
	void writing();
};
Logger::Logger()
{
    out=fopen("Log.txt","w");
}
Logger::~Logger()
{
    writing();
    fclose(out);
}
void Logger::adding(onelog log)
{
    log_array.push_back(log);
}
Logger& Logger::operator<<(onelog log){
    log_array.push_back(log);
    return *this;
}
void Logger::writing()
{
   for(int i=0; i < log_array.size(); i++){
        fprintf(out,"%s",log_array[i].type());
        fprintf(out,"%s\n",log_array[i].message);

    }
    log_array.clear();
}

 Logger *logg=new Logger();


int* array;
int arr_len;
long long filesize;



int check(char c)
{
    if (((c>='0')&&(c<='9'))) return 1; else
    return 0;
}
HANDLE hFileMappingIN;
   HANDLE hFileIN;
   HANDLE hFileMappingOUT;
   HANDLE hFileOUT;
   PCHAR pBufIN;
  LPTSTR pBufOUT;

void getnumbers( PCHAR instr)
{
  char *str;
  char *last;
  //char delim[]=",";
  //printf("%s",pBufIN);
  int numsize=0;
  long long num=0;
  int i=0;
  while (instr[i]==' ') i++;

  while (instr[i]!='\0')
  {
   // printf("%c",instr[i]);
    if (instr[i]>='0' && instr[i]<='9')
    {
        if (++numsize<max_numb_length+1)
        {
            num*=10;
            num+=instr[i]-'0';
        }
        if (num>INT_MAX)
        {
            /*give_msg(ERR,"Numbers are out if range");
            (*logg).~Logger();
            exit(1);*/
            num=num/10;
            while (check(instr[i])) i++;
        }

    }
    else if ((instr[i]==',')||(instr[i]=='\0'))
    {
        if ((instr[i]==',')&&(instr[i-1]==','))
        {
            give_msg(ERR,"File is not .csv format");
        (*logg).~Logger();
        exit(1);
        }
        array[arr_len]=num;
        arr_len++;
        num=0;
        numsize=0;
    }
    else if (instr[i]!='\0')
    {
        give_msg(ERR,"File is not .csv format");
        (*logg).~Logger();
        exit(1);
    }
    i++;

  }

  if (instr[i-1]!=',')
  {
    array[arr_len]=num;
    arr_len++;
  }
  //printf("%s\n",instr);
  /*if ((k==)||(k==(-INT_MAX-1))) {give_msg(ERR,"Numbers are out if range"); (*logg).~Logger(); exit(1); }

  array[arr_len]=k;
  arr_len++;
    if ((*(last)!=',')&&(*last!='\0')) {give_msg(ERR,"File is not .csv format"); (*logg).~Logger(); exit(1); }

  if (*last==',') instr=last+1;
                else instr=last;
  if ((*(last)==',')&&(*(last+1)=='\0')) {give_msg(ERR,"File is not .csv format"); (*logg).~Logger(); exit(1); }
    if(*(instr)==',') {give_msg(ERR,"File is not .csv format"); (*logg).~Logger(); exit(1); }*/


}

int compare(const void *one, const void *two)
{
    int a = *((int*)one);
    int b = *((int*)two);
    if (a<b)
       return -1;
    if (a == b)
       return 0;
    return 1;

}

PCHAR mapIN(char* stdPath, HANDLE hFileMapping, HANDLE hFile, int attribut)
{
    PCHAR pBuf;
    struct _stat stat_buf;
    int rc = _stat(stdPath, &stat_buf);
    if (rc == -1)
    {
        give_msg(ERR,"Error reading input file");
          (*logg).~Logger();
        exit(0);
    }
    filesize = stat_buf.st_size; //getting input file size
    if (filesize == 0)
    {
        give_msg(ERR,"Input file is empty");
          (*logg).~Logger();
        exit(0);
    }


    if (filesize>max_filesize)
    {
        give_msg(ERR,"Unfortunately this program can't sort so big files:(");
          (*logg).~Logger();
        exit(0);
    }
    //printf("%s",stdPath);
   hFile = CreateFile(stdPath,               // file to open
                       GENERIC_READ  ,          // open for reading
                       FILE_SHARE_READ,       // share for reading
                       NULL,                  // default security
                       OPEN_EXISTING,         // existing file only
                       FILE_ATTRIBUTE_NORMAL, // normal file
                       NULL);                 // no attr. template

 if (hFile == INVALID_HANDLE_VALUE)
    {
        give_msg(ERR,"Could not open input file");
        (*logg).~Logger(); exit(2);       // return;
    }
 give_msg(INF,"file is open, size of file is %lld bytes",filesize);

  hFileMapping = CreateFileMapping(hFile,
  NULL, PAGE_READONLY, 0, 0, 0);
 if (hFileMapping == NULL)
   {
      give_msg(ERR,"Could not create input file mapping object");
       (*logg).~Logger(); exit(2);

            // return;
   }

 pBuf = (PCHAR) MapViewOfFile(hFileMapping,   // handle to map object
         FILE_MAP_READ, // read/write permission
         0,
         0,
         0);
    if (pBuf == NULL){
    give_msg(ERR,"MapViewOfFile");
     (*logg).~Logger(); exit(2);
    }
        return pBuf;

}




int main(int argc, char* argv[]) {
  int i, j, n;
  if (argc!=3)
  {
      give_msg(ERR,"number of arguments is incorrect");
     (*logg).~Logger(); exit(1);
  }


    char* inpath = argv[1];
    char* outpath = argv[2];




  time_t start_time;
        time(&start_time);

  //char* inpath = "in.csv"; char* outpath = "out.csv";
 pBufIN=mapIN(inpath,hFileMappingIN,hFileIN,3);
 give_msg(INF,"View of input file successfully mapped");
 //printf("%s",pBufIN);
 array=new int[max_arraysize+1];
 arr_len=0;

  getnumbers(pBufIN);
  //printf("%d ",arr_len);
  give_msg(INF,"Array has been succesfully created");

  qsort(&array[0], arr_len, sizeof(int), compare);
  hFileOUT = CreateFile(outpath,               // file to open
                       GENERIC_WRITE  ,          // open for reading
                       FILE_SHARE_WRITE,       // share for reading
                       NULL,                  // default security
                       CREATE_ALWAYS,         // existing file only
                       FILE_ATTRIBUTE_NORMAL, // normal file
                       NULL);                 // no attr. template

 if (hFileOUT == INVALID_HANDLE_VALUE)
    {
        give_msg(ERR,"Could not open output file");
        (*logg).~Logger(); exit(2);       // return;
    }

    int numb=arr_len/chunk_size+1;
   // printf("%d",numb);
  // printf("%d",arr_len%chunk_size);
for (int ii=0; ii<numb-1; ii++)
  {
  char buffer[max_numb_length+1];
  j=0;
  char* output=new char[chunk_size*(max_numb_length+1)];
  long long pos=0;


 for(i=ii*chunk_size ;i<(ii+1)*chunk_size ;i++) {

   sprintf(buffer, "%d,\0", array[i]);
   if (i==arr_len-1) buffer[strlen(buffer)-1]='\0';
   int length=strlen(buffer);
   for (int j=0; j<length; j++)
    output[pos+j]=buffer[j];
    pos+=length;

  }

   DWORD bytesWritten;
    WriteFile(hFileOUT, output, pos, &bytesWritten, 0);

free(output);
  }


  char buffer[12];
  j=0;
  char* output=new char[chunk_size*(max_numb_length+1)];
  long long pos=0;


 for(i=(numb-1)*chunk_size ;i<arr_len ;i++) {

   sprintf(buffer, "%d,\0", array[i]);
   if (i==arr_len-1) buffer[strlen(buffer)-1]='\0';
   int length=strlen(buffer);
   for (int j=0; j<length; j++)
    output[pos+j]=buffer[j];
    pos+=length;

  }

   DWORD bytesWritten;
    WriteFile(hFileOUT, output, pos, &bytesWritten, 0);

free(output);
    //j += strlen(buffer);

   time_t end_time;
        time(&end_time);
        double duration = end_time - start_time;
  give_msg(INF,"Time estimated: %f seconds",duration);
  (*logg).~Logger();
  UnmapViewOfFile(pBufIN);

  CloseHandle(hFileMappingIN);
    CloseHandle(hFileIN);

    CloseHandle(hFileOUT);
 free(array);
  return 0;
}
